/********************************************************************
	created:	
	created:	
	file base:	Value
	file ext:	h
	author:		Kim,Jong-Kook
				d0nzs00n@naver.com  
				twitter.com/smok95
	purpose:	
	history:
		20110406 add Relationship
*********************************************************************/
#ifndef _TWITTER_VALUE_H_
#define _TWITTER_VALUE_H_

#ifndef __deprecated
#define __deprecated
#endif

#include "CommonMacros.h"
#include "response.h"
#include <vector>
#include <string>
#include "convert.h"
#include "util.h"
#include <twit/resource/twit4c/TwitUtil.h>
#include <time.h>

#if defined(WIN32)||defined(_WIN32)
#include <ObjBase.h>
#else
#  ifndef interface
#     define interface struct
#  endif /*interface*/
#endif /*WINDOWS*/

NS_BEGIN(Twit)
typedef void (*parse_func)(const std::string& src, OUT void* outValue);

namespace Parser
{
   namespace Json
   {
	   class CTJP;
   }
}
#define __friend friend class Parser::Json::CTJP;
NS_BEGIN(Value)

struct Base
{
   virtual void clear()=0;
   virtual const bool isNull(){ return false; };
};

template <typename T>
class Array
{
protected:
   std::vector<T> _v;
public:
	
	void operator =(const Array& o)
	{
		if(this != &o)
		{
			this->clear();
			this->_v = o._v;
		}
	}
   inline void reserve(const size_t _Count)
   {
      _v.reserve(_Count);
   }
   
   virtual inline void clear()
   {
      _v.clear();
   }

   inline bool empty() const 
   {
      return _v.empty();
   }

   inline void push_back(const T& _Val)
   {
      _v.push_back(_Val);
   }

   inline size_t size() const
   {
      return _v.size();
   }
   
   inline T& operator[](const size_t index)
   {
      return _v[index];
   }

   inline const T& operator[](const size_t index) const 
   {
      return _v[index];
   }
};

/*
template <typename T>
interface IArray
{
   virtual size_t size()=0;
   virtual size_t length()=0;
   virtual T& operator[](const size_t index)=0;
   virtual const T& operator[](const size_t index)const=0; 
};
*/

class Category:public Base
{
   __friend;
   std::string name, slug;
   void clear()
   {
      name.clear();
      slug.clear();
   }
public:
   inline std::string& getName(){ return name; }
   inline std::string& getSlug(){ return slug; }
};

typedef Array<Category> Categories;

/* a permanent unique UniqueID referencing an object, such as user or status (ex.1145445329 (status), 14198354 (user)) */
class UniqueID:public Base
{
   uint64 id;
public:
	UniqueID():id(0){}
	UniqueID(const std::string& v){ id=strtoull(v.c_str(),NULL,10);}
	UniqueID(const char* v){ id=strtoull(v,NULL,10);}
	UniqueID(const uint64& v){ id=v;}
	inline void clear(){ id=0; }
   inline const std::string toString() const{	return id==0 ?"":Util::toString(id);	}
   inline operator const std::string(){	return toString();	}
   inline operator const uint64&(){	return id; }
};
typedef std::vector<UniqueID> IDs;



class UserMention:public Base
{
   __friend;
   uint8          start,end;
   std::string    screen_name;
   std::wstring   name;
   UniqueID      id;
   UserMention():start(0),end(0){};

   void clear()
   {
      start = end = 0;
      screen_name.clear();
      name.clear();
      id.clear();
   };
public:
   uint8           getStart(){ return start; }
   uint8           getEnd(){ return end; }
   std::string&    getScreenName(){ return screen_name; }
   std::wstring&   getName(){ return name; }
   UniqueID&       getId(){ return id; }
};
typedef Array<UserMention> UserMentions;


class Url:public Base
{
   __friend;
   uint8 start, end;
   std::string 
      url_,
      expanded_url;
   Url():start(0),end(0){};
   void clear()
   {
      start = end = 0;
      url_.clear();
      expanded_url.clear();
   }

public:
   uint8              getStart(){ return start; }
   uint8              getEnd(){ return end; }
   std::string&       getUrl(){ return url_; }
   std::string&       getExpandedUrl(){ return expanded_url; }
};
typedef Array<Url> Urls;


class HashTag:public Base
{
   __friend;
   uint8 start, end;
   std::wstring text;
   HashTag():start(0),end(0){};
   void clear()
   {
      start = end = 0;
      text.clear();
   }

public:
   uint8           getStart(){ return start; }
   uint8           getEnd(){ return end; }
   std::wstring&   getText(){ return text; }
};
typedef Array<HashTag> HashTags;


class Entities:public Base
{
   __friend;
   Urls urls;
   HashTags hashtags;
   UserMentions user_mentions;
public:
   void clear()
   {
      urls.clear();
      hashtags.clear();
      user_mentions.clear();
   }
   ~Entities()
   {
      clear();
   }
   
public:
   const bool isNull() const 
   {
      return urls.empty() && hashtags.empty() && user_mentions.empty();
   }

   inline Urls&           getUrls(){ return urls; }
   inline HashTags&       getHashTags(){ return hashtags; }
   inline UserMentions&   getUserMentions(){ return user_mentions; }
};

class Time:public Base
{
   time_t _t;
public:
	void clear(){ _t=0; }
   Time():_t(0){}
   Time(const time_t& t):_t(t){}
   Time(const char* s){ _t = TwitUtil_string_to_time_t(s); }
   Time(const std::string& s){ _t = TwitUtil_string_to_time_t(s.c_str()); }
   	
   const std::string toString() const
   {
	   char buf[31]={0,};
	   TwitUtil_strftime(buf,31,"%a %b %d %H:%M:%S +0000 %Y", TwitUtil_gmtime(&_t));
	   return buf;
   }
   inline operator const std::string(){ return toString(); }
   operator const time_t(){ return _t; }
   operator const struct tm(){ return *TwitUtil_gmtime(&_t); };
};

class Color:public Base
{
   uint8 red,  green,   blue;
public:
   void clear()
   {
      red = green = blue = 0;
   }
   Color(uint8 r=0x0, uint8 g=0x0, uint8 b=0x0):red(r), green(g), blue(b){};
   Color(const std::string& hexRgb)
   {
	   if(hexRgb.empty())
	   {
		   clear();
		   return;
	   }
	   this->Color::Color(hexRgb.c_str());
   }
   Color(const char* hex_rgb)
   {
	   if(!hex_rgb)
	   {
		   clear();
		   return;
	   }
	   red   = Convert::HexToByte(hex_rgb[0])*16 + Convert::HexToByte(hex_rgb[1]);
	   green = Convert::HexToByte(hex_rgb[2])*16 + Convert::HexToByte(hex_rgb[3]);
	   blue  = Convert::HexToByte(hex_rgb[4])*16 + Convert::HexToByte(hex_rgb[5]);
   }
   const std::string toString()
   {
	   char buf[10]={0,};
	   TwitUtil_rgb_to_string(red, green, blue, buf);
	   return std::string(buf);
   }
   inline int Red(){ return red; }
   inline int Green(){ return green; }
   inline int Blue(){ return blue; }
   inline operator const std::string()
   {
      return toString();
   }
}; 



class Coordinate:public Base
{
   __friend;
   double latitude, longitude;
public:
   Coordinate():latitude(1000L),longitude(1000L){};
   Coordinate(double _lat, double _long):latitude(_lat), longitude(_long){};
   void clear()
   {
      latitude = longitude = 1000L; 
   }
public:
   double getLatitude(){ return latitude; }
   double getLongtitude(){ return longitude; }
   const bool isNull()
   {
      // latitude range -90.0 to +90.0
      // longitude range -180.0 to +180.0
      return (latitude > 90L || latitude < -90L) && (longitude > 180L || longitude < -180L);
   }
};
typedef Array<Coordinate> Coordinates;

class Status;
class User:public Base
{
   __friend;

   struct data{
	   Time created_at; /*  UTC timestamp of element creation, either status or user */
	   std::string
		   profile_image_url, /*location to a user's avatar file*/
		   url,  /* user's homepage */
		   screen_name,   /* display name of the user that sent a direct message (see screen_name) */
		   profile_background_image_url, /* location of a user's background image */
		   lang, /* the language a user has chosen on Twitter.com for his or her account */
		   time_zone;  /* a user's Time zone */
	   std::wstring
		   name,       /* the user specified name for a saved search */
		   location,   /* user specified string representing where they are from */
		   description;/* 160 characters or less of text that describes a user */
	   UniqueID id;

	   bool
		   protected_,  /* boolean indicating if a user has a protected profile */
		   profile_background_tile,   /* boolean indicating if a user's background is tiled */
		   profile_use_background_image,
		   notifications, /* boolean indicating if a user is receiving device updates for a given user (empty (Default), true, false) */
		   geo_enabled,   /* whether an individual user has enabled geotagging support on his or her account */
		   verified, /* indicates if a user is verified. About verified accounts. (default:false)*/
		   following, /* boolean indicating if the referenced user is following another user. */
		   contributors_enabled, /* whether or not a user has enabled contributors access to his or her account */
		   follow_request_sent, /* True if the user you are authenticating as has requested to follow this user. */
		   show_all_inline_media;
	   int
		   followers_count,  /* number of users following a user's updates */
		   friends_count, /* number of users a user is following */
		   favourites_count, /* number of statuses a user has marked as favorite */
		   utc_offset, /* number of seconds between a user's registered Time zone and Coordinated Universal Time */
		   statuses_count, /* the total number of status updates performed by a user, excluding direct messages sent */
		   listed_count;/* number of public lists in which the user appears. */
	   Color
		   profile_background_color,  /* hex RGB Value for a user's background color */
		   profile_text_color,        /* ex RGB Value for a user's text color */
		   profile_link_color,        /* hex RGB Value for a user's link color */
		   profile_sidebar_fill_color,/* hex RGB Value for a user's sidebar color */
		   profile_sidebar_border_color; /* hex RGB Value for a user's border color */

	   data():profile_sidebar_fill_color(0xe0,0xff,0x92),
		   profile_background_color(0x9a,0xe4,0xe8),
		   profile_link_color(0x00,0x00,0xff),
		   profile_sidebar_border_color(0x87,0xbc,0x44),
		   followers_count(0),
		   friends_count(0),
		   favourites_count(0),
		   listed_count(0),
		   utc_offset(-21600),
		   protected_(false), 
		   profile_background_tile(false),
		   profile_use_background_image(false),
		   notifications(false), 
		   geo_enabled(false),
		   verified(false), 
		   following(false),
		   contributors_enabled(false),
		   follow_request_sent(false), 
		   show_all_inline_media(false){}
   };
	
   data d;
   Status* status;
public:
	User():status(NULL){}
	User(const User& o);
	~User(){ clear(); }
	void clear();
	User& operator=(const User& o);

public:
   inline Time&          getCreatedAt(){ return d.created_at; }
   inline std::string&   getProfileImageUrl(){ return d.profile_image_url; }
   inline std::string&   getUrl(){ return d.url; }
   inline std::string&   getScreenName(){ return d.screen_name; }
   inline std::string&   getProfileBackgroundImageUrl(){ return d.profile_background_image_url; }
   inline std::string&   getLanguage(){ return d.lang; }
   inline std::string&   getTimeZone(){ return d.time_zone; }
   inline std::wstring&  getName(){ return d.name; }
   inline std::wstring&  getLocation(){ return d.location; }
   inline std::wstring&  getDescription(){ return d.description; }

   inline UniqueID&      getId(){ return d.id; }

   inline bool           isProtected(){ return d.protected_; }
   inline bool           isProfileBackgroundTiled(){ return d.profile_background_tile; }
   inline bool           isProfileUseBackgroundImage(){ return d.profile_use_background_image; }
   inline bool           isNotifications(){ return d.notifications; }
   inline bool           isGeoEnabled(){ return d.geo_enabled; }
   inline bool           isVerified(){ return d.verified; }
   inline bool           isFollowing(){ return d.following; }
   __deprecated inline bool isFollowling(){ return d.following; }
   inline bool           isContributorsEnabled(){ return d.contributors_enabled; }
   inline bool           isFollowRequestSent(){ return d.follow_request_sent; }
   inline bool           isShowAllInlineMedia(){ return d.show_all_inline_media; }

   inline int            getFollowersCount(){ return d.followers_count; }
   inline int            getFriendsCount(){ return d.friends_count; }
   inline int            getFavouritesCount(){ return d.favourites_count; }
   inline int            getUtcOffset(){ return d.utc_offset; }
   inline int            getStatusesCount(){ return d.statuses_count; }

   inline Color&         getProfileBackgroundColor(){ return d.profile_background_color; }
   inline Color&         getProfileTextColor(){ return d.profile_text_color; }
   inline Color&         getProfileLinkColor(){ return d.profile_link_color; }
   inline Color&         getProfileSidebarFillColor(){ return d.profile_sidebar_fill_color; }
   inline Color&         getProfileSidebarBorderColor(){ return d.profile_sidebar_border_color; }

   inline Status*  getStatus(){ return status; }
};
typedef Array<User> Users;


class DirectMessage:public Base
{
   __friend;
   Time created_at;
   std::string
      sender_screen_name,
      recipient_screen_name;
   std::wstring
      text;
   UniqueID
      sender_id,
      recipient_id,
      id;
   User
      sender,
      recipient;
public:
   void clear()
   {
	   created_at.clear();
	   sender_screen_name = "";
	   recipient_screen_name = "";
	   text = L"";
	   sender_id.clear();
	   recipient_id.clear();
	   id.clear();
	   sender.clear();
	   recipient.clear();
   }

   inline Time&         getCreatedAt(){ return created_at; }
   inline std::string&  getSenderScreenName(){ return sender_screen_name; }
   inline std::string&  getRecipientScreenName(){ return recipient_screen_name; }
   inline std::wstring& getText(){ return text; }
   inline UniqueID&     getId(){ return id; }
   inline UniqueID&     getSenderId(){ return sender_id; }
   inline UniqueID&     getRecipientId(){ return recipient_id; }
   inline User&         getSender(){ return sender; }
   inline User&         getRecipient(){ return recipient; }
};
typedef Array<DirectMessage> DirectMessages;

class Place;
typedef Array<Place> Places;

class Place:public Base
{
   __friend;
   struct data
   {
	   std::wstring
		   name,
		   country,
		   full_name;
	   std::string
		   country_code,
		   url,
		   id,
		   place_type;
	   Coordinates bounding_box;
	   void clear()
	   {
		   name.clear(), country.clear(), full_name.clear();
		   country_code.clear(), url.clear(), id.clear(), place_type.clear();
		   bounding_box.clear();
	   }
   };
   data d;
   Places*     pcw;	// pContained_within

   Places& _Places(){ return *pcw; }
public:
	Place():pcw(new Places){}
	Place(const Place& o):d(o.d), pcw(o.pcw?new Places(*(o.pcw)):NULL){}
	~Place(){ clear(); }
   void clear()
   {
	   d.clear();
	   _DELETE(pcw);
   }
public:
   std::wstring&      getName(){ return d.name; }
   std::wstring&      getCountry(){ return d.country; }
   std::wstring&      getFullName(){ return d.full_name; }
   std::string&       getCountryCode(){ return d.country_code; }
   std::string&       getUrl(){ return d.url; }
   std::string&       getId(){ return d.id; }
   std::string&       getPlaceType(){ return d.place_type; }
   Places&            getContainedWithin(){ return *pcw; }
};


class Status:public Base
{
   __friend;   
   struct data
   {
		Time created_at;  // UTC timestamp of element creation, either StatusImpl or user (ex.Sat Jan 24 22:14:29 +0000 2009)
		UniqueID
			id,
			in_reply_to_status_id, // unique id for the StatusImpl a StatusImpl replies to (see id)
			in_reply_to_user_id; // unique id for the user that wrote the StatusImpl a StatusImpl replies to (see id)
		std::string
			in_reply_to_screen_name; // display name for the user that wrote the StatusImpl a StatusImpl replies to (see screen_name)
		std::wstring
			source,              // application that sent a StatusImpl   (ex. web (Default), <a href="http://www.tweetdeck.com/">TweetDeck</a>)
			text;                // escaped and HTML encoded StatusImpl body
		bool
			truncated,           // boolean indicating if a StatusImpl required shortening
			favorited,           // boolean indicating if a StatusImpl has been marked as a favorite by the authenticating user
			retweeted;           /* represents whether the user you are authenticating as has retweeted this 
						   StatusImpl or not. The field is a boolean and can be true or false. */
		int
			retweet_count;       /* represents the number of times a StatusImpl has been retweeted using the Twitter 
								retweet action. This field is an integer. There will not be a value for this 
								field when the feature is turned off, or the Tweet was created before we 
								added retweet_count support. */
		Entities     entities;
		IDs contributors;
		data():truncated(false),favorited(false),retweeted(false){}
   };
   data			d;
   User        *pu;		// pUser
   Status      *prs;	// pRetweeted_status
   Place       *pp;		// pPlace
   Coordinate  *pg;		// pGeo
   Coordinate  *pc;		// pCoordinates

#define clsreplace(cls,name)	_DELETE(name); name = new cls(); return *name
   User& _User()				{ clsreplace(User,pu); }
   Status& _Status()			{ clsreplace(Status,prs); }
   Place& _Place()				{ clsreplace(Place, pp); }
   Coordinate& _Geo()			{ clsreplace(Coordinate, pg); }
   Coordinate& _Coordinates()	{ clsreplace(Coordinate, pc); }

public:
   inline void clear()
   {
	   _DELETE(prs);
	   _DELETE(pu);
	   _DELETE(pp);
	   _DELETE(pc);
	   _DELETE(pg);
   }
   Status():prs(NULL), pu(NULL), pp(NULL), pc(NULL), pg(NULL){}
   ~Status(){ clear(); }
   Status(const Status& o):d(o.d), 
	   prs(o.prs?new Status(*(o.prs)):NULL),
	   pp(o.pp?new Place(*(o.pp)):NULL),
	   pu(o.pu?new User(*(o.pu)):NULL),
	   pc(o.pc?new Coordinate(*(o.pc)):NULL),
	   pg(o.pg?new Coordinate(*(o.pg)):NULL){}

	Status& operator=(const Status& o)
	{
		if(this != &o)
		{
			this->d = o.d;
			this->pc = o.pc ? new Coordinate(*(o.pc)):NULL;
			this->pg = o.pg ? new Coordinate(*(o.pg)):NULL;
			this->pp = o.pp ? new Place(*(o.pp)):NULL;
			this->prs = o.prs ? new Status(*(o.prs)):NULL;
			this->pu = o.pu ? new User(*(o.pu)):NULL;
		}
		return *this;
	}

public:
   Time&          getCreatedAt(){ return d.created_at; }
   UniqueID&      getId(){ return d.id;  }
   UniqueID&      getInReplyToStatusId(){ return d.in_reply_to_status_id; }
   UniqueID&      getInReplyToUserId(){ return d.in_reply_to_user_id;  } 
   std::string&   getInReplyToScreenName(){ return d.in_reply_to_screen_name; }
   std::wstring&  getSource(){ return d.source; }
   std::wstring&  getText(){ return d.text; }

   bool           isTruncated(){ return d.truncated; }
   bool           isFavorited(){ return d.favorited; }
   bool           isRetweeted(){ return d.retweeted; }

   int            getRetweetCount(){ return d.retweet_count; }
   User*          getUser(){ return pu; }
   Status*        getRetweetedStatus(){ return prs; }
   Place*         getPlace(){ return pp; }
   Entities&      getEntities(){ return d.entities; }
   IDs&  getContributors(){ return d.contributors; }
};
typedef Array<Status> Statuses;

inline void User::clear(){ _DELETE(status);}
inline User::User(const User& o):d(o.d), status(o.status?new Status(*(o.status)):NULL){}
inline User& User::operator=(const User& o)
{
	if(this != &o)
	{
		this->d = o.d;
		this->status = o.status ? new Status(*(o.status)):NULL;
	}
	return *this;
}


class Trend:public Base
{
   __friend;
   std::string
      url;
   std::wstring
      name,
      query;
public:
   ~Trend()
   {
      clear();
   }
   void clear()
   {
      url.clear();
      name.clear();
      query.clear();
   }

public:
   inline std::string&   getUrl(){ return url; }
   inline std::wstring&  getName(){ return name; }
   inline std::wstring&  getQuery(){ return query; }
};

class Trends:public Array<Trend>
{
   __friend;
   Time
      trend_at,
      as_of;
public:
   ~Trends()
   {
      clear();
   }
   void clear()
   {
      as_of.clear();
      trend_at.clear();
      Array<Trend>::clear();
   }
   
public:
   inline Time&                getTrendAt(){ return trend_at; }
   inline Time&                getAsOf(){ return as_of; }
};
typedef Array<Trends> TrendsList;





class List:public Base
{
	__friend;
protected:
   std::string
      mode,
      uri,
      full_name;

   std::wstring
      slug,
      description,
      name;

   bool
      following;

   int 
      member_count,
      subscriber_count;
   UniqueID
      id;
   User
      user;
public:
   List():
      following(false),
         member_count(0),
         subscriber_count(0)
      {
      };

   void clear()
   {
      user.clear();
      id.clear();
   }

public:
   inline std::string&  getMode(){ return mode; }
   inline std::string&  getUri(){ return uri; }
   inline std::string&  getFullName(){ return full_name; }
   inline std::wstring& getSlug(){ return slug; }
   inline std::wstring& getDescription(){ return description; }
   inline std::wstring& getName(){ return name; }
   inline bool          isFollowing(){ return following; }
   inline int           getMemberCount(){ return member_count; }
   inline int           getSubscriberCount(){ return subscriber_count; }
   inline UniqueID&     getId(){ return id; }
   inline User*         getUser(){ return user.isNull()?NULL:&user; }
};

class Lists:public Array<List>
{
   __friend;
   int
      next_cursor,
      previous_cursor;
   std::string
      next_cursor_str,
      previous_cursor_str;

public:
   Lists():
      next_cursor(0),
      previous_cursor(0),
      next_cursor_str("0"),
      previous_cursor_str("0")
   {

   }

   inline int          getNextCursor(){ return next_cursor; }
   inline int          getPreviousCursor(){ return previous_cursor; }
   inline std::string& getNextCursorString(){ return next_cursor_str; }
   inline std::string& getPreviousCursorString(){ return previous_cursor_str; }
};


class Relationship:public Base
{
	__friend;
	struct Common
	{
		UniqueID id;
		bool 
			following,
			followed_by;
		std::string screen_name;
	};

	Common target;
	struct Source:Common
	{
		bool 
			marked_spam,
			blocking,
			notifications_enabled,
			want_retweets,
			all_replies,
			can_dm;
	};
	Source source;
public:
	void clear()
	{
		// add code
		Common& r = target;
		r.id.clear();
		r.screen_name = "";
		r.following = r.followed_by = false;

		Source& s = source;
		s.id.clear();
		s.screen_name = "";
		s.following = s.followed_by = false;
		s.all_replies = s.blocking = s.can_dm = s.marked_spam = s.notifications_enabled = 
			s.want_retweets = false;
	}
	
	UniqueID& getTargetId(){ return target.id; }
	std::string& getTargetScreenName(){ return target.screen_name; }
	bool isTargetFollowingSource(){ return target.following; }
	bool isTargetFollowedBySource(){ return target.followed_by; }

	UniqueID& getSourceId(){ return source.id; }
	std::string& getSourceScreenName(){ return source.screen_name; }
	bool isSourceFollowingTarget(){ return source.following; }
	bool isSourceFollowedByTarget(){ return source.followed_by; }

	bool isSourceNotificationsEnabled(){ return source.notifications_enabled; }
	bool isSourceBlockingTarget(){ return source.blocking; }
};

class Bool:public Base
{
	__friend;
	int _bool;
public:
	Bool():_bool(0){};
	void clear(){ _bool = 0; }
	const bool isNull(){ return _bool==-1; }
	operator const bool(){ return _bool>0; }
	bool operator ==(const Bool& other){ return this->_bool==other._bool; }
	bool operator !=(const Bool& other){ return this->_bool!=other._bool; }
	bool operator ==(const bool& other){ return (_bool>0)==other; }
	bool operator !=(const bool& other){ return (_bool>0)!=other; }
};
NS_END;
NS_END;

#endif /*_TWITTER_VALUE_H_*/
